package com.project.base.common;

import redis.clients.jedis.HostAndPort;
import redis.clients.jedis.JedisCluster;
import redis.clients.jedis.JedisPoolConfig;

import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.util.*;

/**
 * Redis访问工具类，单例实现，仅适用于Redis集群部署
 *
 * @author wxliyong
 */
public class JedisClusterUtils {
    private static Object LOCK = new Object();
    /**
     * 存储Map的所有key值的后缀
     */
    private static JedisClusterUtils instance;
    private JedisCluster jedisCluster;
    private static Properties properties = getProperties("redis.properties");

    private JedisClusterUtils(JedisCluster jedisCluster) {
        this.jedisCluster = jedisCluster;
    }

    public long del(String keys) {
        Long result = jedisCluster.del(keys);
        if (result == null) {
            return 0L;
        }
        return result;
    }

    public String set(String key, String value) {
        String result = jedisCluster.set(key, value);
        return result;
    }

    /**
     * 将Map对象存入Redis，若是空Map将不做任何处理
     *
     * @param hash 所有value值必须是可序列化的
     */
    public String hmset(String key, Map<String, Object> hash) {
        Map<String, String> jedisMap = new HashMap<String, String>();
        Set<String> keySet = hash.keySet();
        if (keySet.size() != 0) {
            Iterator<String> it = keySet.iterator();
            String[] keys = new String[keySet.size()];
            int index = 0;
            while (it.hasNext()) {
                String hashKey = it.next();
                keys[index++] = hashKey;
                try {
                    jedisMap.put(hashKey, SerializeUtils.serialize(hash.get(hashKey)));
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        } else {
            // 若是入参中的Map为空，则默认是为了将Map清空，那么就直接删除该KEY
            jedisCluster.del(key);
            return "OK";
        }
        return jedisCluster.hmset(key, jedisMap);
    }

    /**
     * 将Map对象存入Redis，若是空Map将不做任何处理
     *
     * @param hash 所有value值必须是可序列化的
     */
    public String hmset(String key, Map<String, Object> hash, int seconds) {
        String result = hmset(key, hash);
        if ("OK".equals(result)) {
            jedisCluster.expire(key, seconds);
        }
        return result;
    }

    public void hmdel(String key) {
        jedisCluster.del(key);
    }

    public Map<String, Object> hmget(String key) {
        Map<String, Object> result = new HashMap<String, Object>();
        Map<String, String> redisResult = jedisCluster.hgetAll(key);
        Iterator<String> it = redisResult.keySet().iterator();
        while (it.hasNext()) {
            String mapKey = it.next();
            String value = redisResult.get(mapKey);
            Object valueObj = null;
            if (value != null) {
                valueObj = SerializeUtils.deserialize(value);
            }
            result.put(mapKey, valueObj);
        }
        return result;
    }

    /**
     * 存储一对key-value，并设置过期时间
     *
     * @param key
     * @param value
     * @param seconds 有效秒数
     * @return Redis存储结果，OK-存储成功
     */
    public String set(String key, String value, int seconds) {
        return jedisCluster.setex(key, seconds, value);
    }

    public String get(String key) {
        return jedisCluster.get(key);
    }

    /**
     * 递增计数器
     *
     * @param key
     * @return 递增后的结果值
     */
    public long incr(String key) {
        return jedisCluster.incr(key);
    }

    /**
     * 递减计数器
     *
     * @param key
     * @return 递减后的结果值
     */
    public long decr(String key) {
        return jedisCluster.decr(key);
    }

    public void lpush(String key, String... values) {
        jedisCluster.lpush(key, values);
    }

    public List<String> lget(String key) {
        return lget(key, 0);
    }

    public List<String> lget(String key, long start) {
        Long length = jedisCluster.llen(key);
        if (length == null || length <= 0 || start > length) {
            return new ArrayList<String>();
        }
        return jedisCluster.lrange(key, start, length);
    }

    public static JedisClusterUtils getInstance() {
        if (instance == null) {
            synchronized (LOCK) {
                if (instance == null) {
                    int maxIdle = Integer.parseInt(properties.getProperty("redis.maxIdle"));
                    int minIdle = Integer.parseInt(properties.getProperty("redis.minIdle"));
                    int maxTotal = Integer.parseInt(properties.getProperty("redis.maxTotal"));
                    int maxWaitMillis = Integer.parseInt(properties.getProperty("redis.maxWaitMillis"));
                    JedisPoolConfig config = new JedisPoolConfig();
                    config.setMaxTotal(maxTotal);
                    config.setMaxIdle(maxIdle);
                    config.setMinIdle(minIdle);
                    config.setMaxWaitMillis(maxWaitMillis);

                    String serversStr = properties.getProperty("redis.servers").trim();
                    String[] servers = serversStr.split(",");
                    Set<HostAndPort> jedisClusterNodes = new HashSet<HostAndPort>();
                    for (String server : servers) {
                        String[] hostOrPort = server.split(":");
                        jedisClusterNodes.add(new HostAndPort(hostOrPort[0], Integer.parseInt(hostOrPort[1])));
                    }
                    instance = new JedisClusterUtils(new JedisCluster(jedisClusterNodes, 2000, 2, config));
                }
            }
        }
        return instance;
    }

    /**
     * 根据路径返回配置文件
     *
     * @param path
     * @return
     */
    private static final Properties getProperties(String path) {
        Properties properties = new Properties();
        try {
            InputStream inputStream = JedisClusterUtils.class.getClassLoader().getResourceAsStream(path);
            properties.load(inputStream);
        } catch (FileNotFoundException e) {
        } catch (IOException e) {
            e.printStackTrace();
        }
        return properties;
    }
}
